﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace HMEngine.HMCameras {
    public class HMCamera {
        private Viewport myViewport;

        public Vector3 Position { get; set; }
        public Quaternion Rotation { get; set; }
        public Matrix World { get; private set; }
        public Matrix View { get; private set; }
        public Matrix Projection { get; private set; }
        public float FieldOfView { get; private set; }
        public Vector3 Target { get; set; }
        public Viewport Viewport {
            get { return myViewport; }
            set {
                myViewport = value;
                myViewport.MinDepth = 1.0f;
                myViewport.MaxDepth = 1000.0f;
            }
        }

        public HMCamera(Viewport newViewport) {
            Position = new Vector3(0, 0, 1);
            Rotation = new Quaternion(0, 0, 0, 1);
            FieldOfView = MathHelper.Pi / 3.0f;
            Target = new Vector3(0, 0, 0);

            SetViewport(newViewport);
        }

        public void SetViewport(Viewport newViewport) {
            newViewport.MinDepth = 1.0f;
            newViewport.MaxDepth = 1000.0f;
            
            Viewport = newViewport;
        }

        public void Rotate(Vector3 axis, float angle) {
            axis = Vector3.Transform(axis, Matrix.CreateFromQuaternion(Rotation));
            Rotation = Quaternion.Normalize(Quaternion.CreateFromAxisAngle(axis, angle) * Rotation);

            Update();
        }

        public void Translate(Vector3 distance) {
            Position += Vector3.Transform(distance, Matrix.CreateFromQuaternion(Rotation));

            Update();
        }

        public void Revolve(Vector3 axis, float angle) {
            Vector3 revolveAxis = Vector3.Transform(axis, Matrix.CreateFromQuaternion(Rotation));
            Quaternion rotate = Quaternion.CreateFromAxisAngle(revolveAxis, angle);
            Position = Vector3.Transform(Position - Target, Matrix.CreateFromQuaternion(rotate)) + Target;

            Rotate(axis, angle);
        }

        public void RotateGlobal(Vector3 axis, float angle) {
            Rotation = Quaternion.Normalize(Quaternion.CreateFromAxisAngle(axis, angle) * Rotation);

            Update();
        }

        public void TranslateGlobal(Vector3 distance) {
            Position += distance;

            Update();
        }

        public void RevolveGlobal(Vector3 axis, float angle) {
            Quaternion rotate = Quaternion.CreateFromAxisAngle(axis, angle);
            Position = Vector3.Transform(Position - Target, Matrix.CreateFromQuaternion(rotate)) + Target;

            RotateGlobal(axis, angle);
        }

        public void Update() {
            World = Matrix.Identity;

            View = Matrix.Invert(
                Matrix.CreateFromQuaternion(Rotation) *
                Matrix.CreateTranslation(Position)
            );

            Projection = Matrix.CreatePerspectiveFieldOfView(
                FieldOfView,
                Viewport.AspectRatio,
                Viewport.MinDepth, Viewport.MaxDepth
            );
        }
    }
}